package com.gzhryc.common.xml;

import com.gzhryc.common.ClassTools;
import com.gzhryc.common.StringTools;
import com.gzhryc.common.xml.annotation.XmlAttribute;
import com.gzhryc.common.xml.annotation.XmlElement;
import com.gzhryc.common.xml.annotation.XmlText;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;

public class XmlTools {

    /**
     * 根据类生成XML，默认类的属性为节点的属性
     *
     * @param entity
     * @return
     */
    public static <T> String changToXmlStr(T entity, boolean isUpperCase) {
        String result = null;
        try {
            XmlElement xmlEl = entity.getClass().getAnnotation(XmlElement.class);
            String rootName = entity.getClass().getSimpleName();
            if (xmlEl != null && xmlEl.value() != null && xmlEl.value().length > 0) {
                rootName = xmlEl.value()[0];        //默认取第一个
            } else if (isUpperCase) {
                rootName = entity.getClass().getSimpleName().toUpperCase();
            }

            Document document = DocumentHelper.createDocument();
            Element root = document.addElement(rootName);// 添加根节点

            Field[] fields = ClassTools.getDeclaredFields(entity.getClass());
            setElement(root, entity, fields, isUpperCase);
            result = StringEscapeUtils.unescapeXml(root.asXML());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 解析XML字符串为一个对象
     *
     * @param xml
     * @param clazz
     * @param isUpperCase
     * @return
     */
    public static <T> T changToObject(String xml, Class<T> clazz, boolean isUpperCase) {
        try {
            Document doc = DocumentHelper.parseText(xml);
            Element root = doc.getRootElement();// 获得根节点

            //创建对象
            T entity = clazz.newInstance();

            String typeName = null;
            XmlElement xmlEl = clazz.getAnnotation(XmlElement.class);
            if (xmlEl != null && StringTools.isNotBlank(xmlEl.typeName())) {
                typeName = xmlEl.typeName();
            }

            Field[] fields = ClassTools.getDeclaredFields(clazz);
            Map<String,List<Object>> values = new HashMap<>();      //List属性
            getElement(root, entity, fields, values, typeName,isUpperCase);
            //设置List属性
            for(Field field : fields){
                if (List.class.equals(field.getType())) {
                    List<Object> value = values.get(field.getName());
                    if(value != null){
                        if (!Modifier.isFinal(field.getModifiers())) {
                            field.setAccessible(true);
                        }
                        field.set(entity, value);
                    }
                }
            }
            return entity;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static <T> T changToObject(InputStream inputStream, Class<T> clazz, boolean isUpperCase) {
        if (inputStream != null) {
            try {
                SAXReader saxReadr = new SAXReader(); // 得到SAXReader对象
                saxReadr.setEncoding("UTF-8");
                Document doc = saxReadr.read(inputStream); // 将输入流
                Element root = doc.getRootElement(); // 获得根节点

                //创建对象
                T entity = clazz.newInstance();

                String typeName = null;
                XmlElement xmlEl = clazz.getAnnotation(XmlElement.class);
                if (xmlEl != null && StringTools.isNotBlank(xmlEl.typeName())) {
                    typeName = xmlEl.typeName();
                }

                Field[] fields = ClassTools.getDeclaredFields(clazz);
                Map<String,List<Object>> values = new HashMap<>();      //List属性

                getElement(root, entity, fields, values,typeName,isUpperCase);
                //设置List属性
                for(Field field : fields){
                    if (List.class.equals(field.getType())) {
                        List<Object> value = values.get(field.getName());
                        if(value != null){
                            if (!Modifier.isFinal(field.getModifiers())) {
                                field.setAccessible(true);
                            }
                            field.set(entity, value);
                        }
                    }
                }
                return entity;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    public static <T> T changToObject(Document doc, Class<T> clazz, boolean isUpperCase) {
        try {
            Element root = doc.getRootElement(); // 获得根节点

            //创建对象
            T entity = clazz.newInstance();

            String typeName = null;
            XmlElement xmlEl = clazz.getAnnotation(XmlElement.class);
            if (xmlEl != null && StringTools.isNotBlank(xmlEl.typeName())) {
                typeName = xmlEl.typeName();
            }

            Field[] fields = ClassTools.getDeclaredFields(clazz);
            Map<String,List<Object>> values = new HashMap<>();      //List属性
            getElement(root, entity, fields, values, typeName,isUpperCase);
            //设置List属性
            for(Field field : fields){
                if (List.class.equals(field.getType())) {
                    List<Object> value = values.get(field.getName());
                    if(value != null){
                        if (!Modifier.isFinal(field.getModifiers())) {
                            field.setAccessible(true);
                        }
                        field.set(entity, value);
                    }
                }
            }
            return entity;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static Document buildDocument(InputStream inputStream) {
        try {
            SAXReader saxReadr = new SAXReader(); // 得到SAXReader对象
            saxReadr.setEncoding("UTF-8");
            Document doc = saxReadr.read(inputStream); // 将输入流
            return doc;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 递归获取节点属性并赋予给对象的相应属性
     *
     * @param element
     * @param entity
     * @param fields
     * @param isUpperCase
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    private static void getElement(Element element, Object entity, Field[] fields, Map<String,List<Object>> values,String typeName,boolean isUpperCase)
            throws InstantiationException, IllegalAccessException {
        //属性
        Iterator<?> attributeIterator = element.attributeIterator();
        if (attributeIterator != null) {
            while (attributeIterator.hasNext()) {
                Attribute attribute = (Attribute) attributeIterator.next();
                for (Field field : fields) {
                    if (Modifier.isStatic(field.getModifiers())) {
                        continue;
                    }

                    String name = field.getName();
                    XmlAttribute xmlAttr = field.getAnnotation(XmlAttribute.class);
                    if(xmlAttr != null){
                        name = xmlAttr.value();
                    }

                    if(attribute.getName().equals(name)){
                        if(ClassTools.isBaseType(field.getType())){
                            String typeValue = attribute.getValue();
                            ClassTools.setFieldValue(entity, field, typeValue);
                        }
                        break;
                    }
                }
            }
        }

        //标签内容
        for (Field field : fields) {
            if (Modifier.isStatic(field.getModifiers())) {
                continue;
            }
            XmlText xmlText = field.getAnnotation(XmlText.class);
            if(xmlText != null){
                if(ClassTools.isBaseType(field.getType())){
                    ClassTools.setFieldValue(entity, field, element.getTextTrim());
                }
            }
        }

        //检查是否有自动匹配
        Field autoField = null;
        List<Field> nodeFieldList = new ArrayList<>();
        for (Field field : fields) {
            if (Modifier.isStatic(field.getModifiers())) {
                continue;
            }
            if (!Modifier.isFinal(field.getModifiers())) {
                field.setAccessible(true);
            }
            XmlElement xmlEl = field.getAnnotation(XmlElement.class);
            if (xmlEl != null) {
                boolean flag = true;
                for (String name : xmlEl.value()) {
                    if ("*".equals(name)) {
                        autoField = field;
                        flag = false;
                        break;
                    }
                }
                if (flag) {
                    nodeFieldList.add(field);
                }
            }else if(StringTools.isNotBlank(typeName)){
                //设置标签名称
                if(typeName.equals(field.getName())){
                    if(ClassTools.isBaseType(field.getType())){
                        String typeValue = element.getName();
                        if (isUpperCase) {
                            typeValue = element.getName().toUpperCase();
                        }
                        ClassTools.setFieldValue(entity, field, typeValue);
                    }
                }
            }
        }

        Iterator<?> iter = element.elementIterator();
        if (iter != null) {
            while (iter.hasNext()) {
                Element childEl = (Element) iter.next();
                boolean flag = false;
                for (Field field : nodeFieldList) {
                    if (!Modifier.isFinal(field.getModifiers())) {
                        field.setAccessible(true);
                    }
                    XmlElement xmlEl = field.getAnnotation(XmlElement.class);
                    if (xmlEl != null) {
                        if(xmlEl.value() == null || xmlEl.value().length == 0){
                            String name = field.getName();
                            if (isUpperCase) {
                                name = field.getName().toUpperCase();
                            }
                            if (name.equals(childEl.getName())) {
                                //匹配成功
                                setFieldValue(childEl,entity,field,values,isUpperCase);
                                flag = true;
                                break;  //设置成功，跳出fields循环
                            }
                        }else {
                            for (String name : xmlEl.value()) {
                                // 获取节点名称
                                if (StringUtils.isNotBlank(name)) {
                                    if (isUpperCase) {
                                        name = name.toUpperCase();
                                    }

                                    if (name.equals(childEl.getName())) {
                                        //匹配成功
                                        setFieldValue(childEl,entity,field,values,isUpperCase);
                                        flag = true;
                                        break;
                                    }
                                }
                            }
                            if(flag){
                                break;  //设置成功，跳出fields循环
                            }
                        }
                    }
                }

                if(!flag){
                    //未设置成功，检查是否有自动
                    if(autoField != null){
                        setFieldValue(childEl,entity,autoField,values,isUpperCase);
                    }
                }
            }
        }
    }

    private static void setFieldValue(Element element, Object entity, Field field, Map<String,List<Object>> values,boolean isUpperCase) throws InstantiationException, IllegalAccessException {
        if (List.class.equals(field.getType())) {
            Class<?> genericClass = null;
            Type fc = field.getGenericType();
            // 是否是泛型参数类型
            if (fc != null && fc instanceof ParameterizedType) {
                ParameterizedType pt = (ParameterizedType) fc;
                genericClass = (Class<?>) pt.getActualTypeArguments()[0];
            }

            List<Object> value = values.get(field.getName());
            if(value == null){
                value = new ArrayList<>();
                values.put(field.getName(),value);
            }

            //List.add()
            if (ClassTools.isBaseType(genericClass)) {       //基础数据类型
                value.add(element.getTextTrim());
            } else {
                Object childObj = genericClass.newInstance();

                String childTypeName = null;
                //设置标签名称
                XmlElement xmlEl = genericClass.getAnnotation(XmlElement.class);
                if (xmlEl != null && StringTools.isNotBlank(xmlEl.typeName())) {
                    childTypeName = xmlEl.typeName();
                }

                Field[] fieldList = ClassTools.getDeclaredFields(genericClass);
                Map<String,List<Object>> childValues = new HashMap<>();     //子节点的List属性
                getElement(element, childObj, fieldList, childValues,childTypeName,isUpperCase);
                //设置List属性
                for(Field childField : fieldList){
                    if (List.class.equals(childField.getType())) {
                        List<Object> childValue = childValues.get(childField.getName());
                        if(childValue != null){
                            if (!Modifier.isFinal(childField.getModifiers())) {
                                childField.setAccessible(true);
                            }
                            childField.set(childObj, childValue);
                        }
                    }
                }
                value.add(childObj);
            }
        } else {
            if(ClassTools.isBaseType(field.getType())){
                ClassTools.setFieldValue(entity, field, element.getTextTrim());
            }else {
                Object childObj = field.getType().newInstance();

                String childTypeName = null;
                //设置标签名称
                XmlElement xmlEl = field.getType().getAnnotation(XmlElement.class);
                if (xmlEl != null && StringTools.isNotBlank(xmlEl.typeName())) {
                    childTypeName = xmlEl.typeName();
                }

                Field[] fieldList = ClassTools.getDeclaredFields(field.getType());
                Map<String, List<Object>> childValues = new HashMap<>();     //子节点的List属性
                getElement(element, childObj, fieldList, values, childTypeName, isUpperCase);
                //设置List属性
                for (Field childField : fieldList) {
                    if (List.class.equals(childField.getType())) {
                        List<Object> childValue = childValues.get(childField.getName());
                        if (childValue != null) {
                            if (!Modifier.isFinal(childField.getModifiers())) {
                                childField.setAccessible(true);
                            }
                            childField.set(childObj, childValue);
                        }
                    }
                }
                field.set(entity, childObj);
            }
        }
    }

    /**
     * 递归给节点赋予属性或子节点
     *
     * @param element
     * @param entity
     * @param fields
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     */
    private static void setElement(Element element, Object entity, Field[] fields, boolean isUpperCase)
            throws IllegalArgumentException, IllegalAccessException {
        for (Field field : fields) {
            if (Modifier.isStatic(field.getModifiers())) {
                continue;
            }
            if (!Modifier.isFinal(field.getModifiers())) {
                field.setAccessible(true);
            }
            Object obj = null;
            if (entity != null) {
                obj = field.get(entity);
            }
            XmlElement xmlEl = field.getAnnotation(XmlElement.class);
            if (xmlEl != null) {
                String elName = null;
                if(xmlEl.value() != null && xmlEl.value().length > 0){
                    elName = xmlEl.value()[0];  //默认第一个
                }
                if (StringUtils.isBlank(elName)) {
                    if (isUpperCase) {
                        elName = field.getName().toUpperCase();
                    } else {
                        elName = field.getName();
                    }
                }
                if (obj != null) {
                    if (List.class.equals(field.getType())) {
                        Class<?> genericClass = null;
                        Type fc = field.getGenericType();
                        // 是否是泛型参数类型
                        if (fc != null && fc instanceof ParameterizedType) {
                            ParameterizedType pt = (ParameterizedType) fc;
                            genericClass = (Class<?>) pt.getActualTypeArguments()[0];
                        }
                        for (Object itemObj : (List<?>) obj) {
                            Element childEl = element.addElement(elName);
                            Class<?> itemClass = genericClass;
                            if (itemClass == null) {
                                itemClass = itemObj.getClass();
                            }
                            if (ClassTools.isBaseType(itemClass)) {
                                childEl.setText(itemObj.toString());
                            } else {
                                Field[] fieldList = ClassTools.getDeclaredFields(itemClass);
                                setElement(childEl, itemObj, fieldList, isUpperCase);
                            }
                        }
                    } else {
                        Element childEl = element.addElement(elName);
                        if (ClassTools.isBaseType(field.getType())) {
                            childEl.setText(obj.toString());
                        } else {
                            Field[] fieldList = ClassTools.getDeclaredFields(field.getType());
                            setElement(childEl, obj, fieldList, isUpperCase);
                        }
                    }
                } else {
                    // 非列表
                    if (!List.class.equals(field.getType())) {
                        Element childEl = element.addElement(elName);
                        if (ClassTools.isBaseType(field.getType())) {
                            childEl.setText("");
                        } else {
                            Field[] fieldList = ClassTools.getDeclaredFields(field.getType());
                            setElement(childEl, obj, fieldList, isUpperCase);
                        }
                    }
                }
            } else {
                XmlText xmlTxt = field.getAnnotation(XmlText.class);
                if (xmlTxt != null) {
                    if (obj != null) {
                        element.setText(obj.toString());
                    }
                } else {
                    if (obj != null) {
                        if (isUpperCase) {
                            element.addAttribute(field.getName().toUpperCase(), obj.toString());
                        } else {
                            element.addAttribute(field.getName(), obj.toString());
                        }
                    } else {
                        if (isUpperCase) {
                            element.addAttribute(field.getName(), "");
                        } else {
                            element.addAttribute(field.getName().toUpperCase(), "");
                        }
                    }
                }
            }
        }
    }

    public static String getAttrValue(Node node, String name) {
        Node attr = node.getAttributes().getNamedItem(name);
        if (attr != null) {
            return attr.getNodeValue();
        }
        return null;
    }

    public static Integer getAttrIntegerValue(Node node, String name) {
        Node attr = node.getAttributes().getNamedItem(name);
        if (attr != null) {
            if (StringUtils.isNotBlank(attr.getNodeValue())) {
                return Integer.valueOf(attr.getNodeValue().trim());
            }
        }
        return null;
    }

    public static String getFirstChildNodeValue(Node node, String name) {
        NodeList children = node.getChildNodes();
        if (children != null) {
            for (int i = 0; i < children.getLength(); i++) {
                Node child = children.item(i);
                if (child.getNodeName().equals(name)) {
                    return child.getNodeValue();
                }
            }
        }
        return null;
    }
}
